#include <QGraphicsView>
#include <QIcon>
#include <QPushButton>
#include <QFile>
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonArray>

#include "plcmodule.h"
#include "ui_mainwindow.h"

PlcModule::PlcModule(Ui::MainWindow* ui, ModbusManager* modbusManager, QObject* parent)
    : QObject(parent), ui_(ui), m_scene(nullptr), modbusManager_(modbusManager)
{
}

void PlcModule::init()
{
    m_scene = new PlcGraphicsScene(ui_->plcGraphicsView, this);
    ui_->plcGraphicsView->setScene(m_scene);

    // 设置场景矩形，确保正确显示
    m_scene->setSceneRect(0, 0, ui_->plcGraphicsView->width(), ui_->plcGraphicsView->height());

    // 设置视图属性确保网格可见，开抗锯齿确保折线的竖线粗细相同
    ui_->plcGraphicsView->setRenderHint(QPainter::Antialiasing, true);
    ui_->plcGraphicsView->setViewportUpdateMode(QGraphicsView::FullViewportUpdate);

    //根据当前 sceneRect / 网格，创建每行 x=0 的锚点
    m_scene->rebuildRowAnchors();

    connect(m_scene, &PlcGraphicsScene::elementCreated, this, &PlcModule::onElementCreated);

    connect(ui_->normallyOpen, &QPushButton::clicked, this, [this]() {
        prepareToCreateElement(PlcInstructionType::NormallyOpen);
    });
    connect(ui_->normallyClosed, &QPushButton::clicked, this, [this]() {
        prepareToCreateElement(PlcInstructionType::NormallyClosed);
    });
    connect(ui_->coil, &QPushButton::clicked, this, [this]() {
        prepareToCreateElement(PlcInstructionType::Coil);
    });
    connect(ui_->greater, &QPushButton::clicked, this, [this]() {
        prepareToCreateElement(PlcInstructionType::Greater);
    });
    connect(ui_->greaterEqual, &QPushButton::clicked, this, [this]() {
        prepareToCreateElement(PlcInstructionType::GreaterEqual);
    });
    connect(ui_->less, &QPushButton::clicked, this, [this]() {
        prepareToCreateElement(PlcInstructionType::Less);
    });
    connect(ui_->lessEqual, &QPushButton::clicked, this, [this]() {
        prepareToCreateElement(PlcInstructionType::LessEqual);
    });
    connect(ui_->equal, &QPushButton::clicked, this, [this]() {
        prepareToCreateElement(PlcInstructionType::Equal);
    });
    connect(ui_->notEqual, &QPushButton::clicked, this, [this]() {
        prepareToCreateElement(PlcInstructionType::NotEqual);
    });
    connect(m_scene, &PlcGraphicsScene::sceneContentChanged, this, [this](){
        m_isModified = true;
    });

    // 连接Modbus信号
    if (modbusManager_)
    {
        connect(modbusManager_, &ModbusManager::coilUpdated, this, &PlcModule::onCoilUpdated);
        connect(modbusManager_, &ModbusManager::registerUpdated, this, &PlcModule::onRegisterUpdated);
    }

    m_dRegisterSnapshot[0] = 0;  // D0 默认值
    m_dRegisterSnapshot[1] = 0;  // D1 默认值
    applyDRegisterSnapshot(m_dRegisterSnapshot);  // 应用默认的寄存器快照，进行初始检查

    //连接元件写线圈信号
    connect(m_scene, &PlcGraphicsScene::elementCreated, this, [this](PlcElement* item){
        connect(item, &PlcElement::writeCoilRequested, this, &PlcModule::onWriteCoilRequested);
    });
}

// 写线圈请求处理
void PlcModule::onWriteCoilRequested(int address, bool value)
{
    if (modbusManager_ && modbusManager_->isRunning())
    {
        modbusManager_->writeCoil(address, value);
    }
}

void PlcModule::prepareToCreateElement(PlcInstructionType type)
{
    m_scene->setMode(PlcGraphicsScene::CreateItem);
    m_scene->setInstructionTypeToCreate(type);
}

void PlcModule::onElementCreated(PlcElement* item)
{
    connect(item, &PlcElement::writeCoilRequested, this, &PlcModule::onWriteCoilRequested);
    // 连接写寄存器信号
    connect(item, &PlcElement::writeRegisterRequested, modbusManager_, &ModbusManager::writeRegister);
    // 连接日志信号
    connect(item, &PlcElement::logMessageRequested, this, [this](const QString& message) {
        // 转发到主窗口的日志系统
        if (modbusManager_)
        {
            modbusManager_->logMessage(message);
        }
    });
    // 添加调试输出 - 打印新创建的元件信息
    qDebug() << "创建新元件 - 类型:" << static_cast<int>(item->instructionType())
             << "前缀:" << item->propPrefix() << "值:" << item->propValue();

    // 如果是线圈类元件，添加到监控列表
    if (item->propPrefix().compare("M", Qt::CaseInsensitive) == 0) {
        int address = item->propValue();
        qDebug() << "添加线圈到监控列表: M" << address;
        modbusManager_->addMonitoredCoil(address);
    }

    // 如果是比较元件，立即使用当前寄存器快照更新状态
    if (item->isComparator()) {
        item->updateCompareStateFromD(m_dRegisterSnapshot);
    }

    // 监听地址变更，维护监控集合
    connect(item, &PlcElement::coilAddressChanged, this, [this](int oldAddr, int newAddr){
        if (!modbusManager_) return;
        modbusManager_->removeMonitoredCoil(oldAddr);
        modbusManager_->addMonitoredCoil(newAddr);
    });
    m_isModified = true; // 创建元件后标记
}

// 添加线圈更新处理
void PlcModule::onCoilUpdated(int address, bool value) {
    // 添加调试输出 - 打印接收到的线圈更新
    qDebug() << "接收到线圈更新 - 地址:" << address << "值:" << value;
    // 更新所有匹配的线圈元件
    const auto itemsList = m_scene->items();
    for (QGraphicsItem* it : itemsList)
    {
        if (it->type() == PlcElement::Type)
        {
            auto* elem = static_cast<PlcElement*>(it);

            // 只处理线圈类元件
            if (elem->propPrefix().compare("M", Qt::CaseInsensitive) == 0 &&
                elem->propValue() == address) {
                elem->setOn(value);
            }
        }
    }
}

// 添加寄存器更新处理
void PlcModule::onRegisterUpdated(int address, quint16 value)
{
    // 更新寄存器快照
    m_dRegisterSnapshot[address] = value;

    // 更新所有显示这个寄存器地址的元件
    updateRegisterDisplay(address, value);

    // 更新所有比较元件
    applyDRegisterSnapshot(m_dRegisterSnapshot);
}

void PlcModule::applyDRegisterSnapshot(const QHash<int, quint16>& dregs)
{
    if (!m_scene)
    {
        return;
    }

    // 遍历场景，找到所有 PlcElement，若为比较元件则根据寄存器快照更新状态
    const auto itemsList = m_scene->items();
    for (QGraphicsItem* it : itemsList)
    {
        if (it->type() == PlcElement::Type)
        {
            auto* elem = static_cast<PlcElement*>(it);
            if (elem->isComparator())
            {
                elem->updateCompareStateFromD(dregs);
            }
        }
    }
}

void PlcModule::updateRegisterDisplay(int address, quint16 value)
{
    if (!m_scene) return;

    // 遍历所有元件，更新显示相同寄存器地址的元件
    const auto itemsList = m_scene->items();
    for (QGraphicsItem* it : itemsList)
    {
        if (it->type() == PlcElement::Type)
        {
            auto* elem = static_cast<PlcElement*>(it);

            // 检查左寄存器是否匹配
            if (elem->propPrefix().compare("D", Qt::CaseInsensitive) == 0 &&
                elem->propValue() == address)
            {
                // 更新左寄存器显示值
                elem->setLeftRegisterValue(value);
            }

            // 检查右寄存器是否匹配
            if (elem->propPrefix().compare("D", Qt::CaseInsensitive) == 0 &&
                elem->propValue2() == address)
            {
                // 更新右寄存器显示值
                elem->setRightRegisterValue(value);
            }
        }
    }
}

// 重置（新建）
void PlcModule::resetDiagram()
{
    if (!m_scene)
    {
        return;
    }

    // 安全清空：清图元 + 清空锚点指针数组
    m_scene->clearAll();

    // 重新生成当前场景尺寸下的行锚点
    m_scene->rebuildRowAnchors();

    m_isModified = false;

    // 重置寄存器快照为默认值
    m_dRegisterSnapshot.clear();
    m_dRegisterSnapshot[0] = 0;  // D0 默认值
    m_dRegisterSnapshot[1] = 0;  // D1 默认值
    // 应用默认的寄存器快照
    applyDRegisterSnapshot(m_dRegisterSnapshot);
}

// 保存
bool PlcModule::saveToFile(const QString& filePath)
{
    if (!m_scene)
    {
        return false;
    }

    // 1、先收集所有元件，建立指针->索引映射
    QJsonArray elemArray;
    QList<PlcElement*> elems;
    QHash<const PlcElement*, int> indexOf;

    for (QGraphicsItem* it : m_scene->items(Qt::AscendingOrder))
    {
        if (it->type() == PlcElement::Type)
        {
            auto* e = static_cast<PlcElement*>(it);
            const int idx = elems.size();
            elems.append(e);
            indexOf.insert(e, idx);

            QJsonObject obj;
            obj["type"]   = static_cast<int>(e->instructionType());
            obj["x"]      = e->pos().x();
            obj["y"]      = e->pos().y();
            obj["w"]      = e->boundingRect().width();
            obj["h"]      = e->boundingRect().height();
            obj["on"]     = e->isOn();
            obj["prefix"] = e->propPrefix();
            obj["v1"]     = e->propValue();
            obj["v2"]     = e->propValue2();

            elemArray.append(obj);
        }
    }

    // 2、再收集“元素-元素 / 元素-锚点”的连线
    QJsonArray connArray;
    for (QGraphicsItem* it : m_scene->items(Qt::AscendingOrder))
    {
        if (it->type() != ConnectionItem::Type)
        {
            continue;
        }
        auto* c = static_cast<ConnectionItem*>(it);
        QJsonObject co;

        // 标出哪一端是锚点
        const bool sA = c->startIsAnchor();
        const bool eA = c->endIsAnchor();
        co["s_anchor"] = sA;
        co["e_anchor"] = eA;

        // 非锚点端存元素索引 + 端口侧（0=Left, 1=Right）
        if (!sA && c->startItem() && indexOf.contains(c->startItem()))
        {
            co["s_idx"]  = indexOf.value(c->startItem());
            co["s_side"] = (c->startSide() == ConnectionItem::Right ? 1 : 0);
        }
        else
        {
            co["s_idx"]  = -1;
            // 保存锚点坐标（场景坐标）
            const QPointF p = c->startAnchorScenePos();
            co["s_ax"] = p.x();
            co["s_ay"] = p.y();
        }

        if (!eA && c->endItem() && indexOf.contains(c->endItem()))
        {
            co["e_idx"]  = indexOf.value(c->endItem());
            co["e_side"] = (c->endSide() == ConnectionItem::Right ? 1 : 0);
        }
        else
        {
            co["e_idx"]  = -1;
            const QPointF p = c->endAnchorScenePos();
            co["e_ax"] = p.x();
            co["e_ay"] = p.y();
        }

        // 折线信息
        co["poly"] = c->isPolyline();
        if (c->isPolyline())
        {
            const QPointF b = c->bendPoint();
            co["bx"] = b.x();
            co["by"] = b.y();
        }

        connArray.append(co);
    }

    QJsonObject root;
    root["elements"]    = elemArray;
    root["connections"] = connArray;

    QJsonDocument doc(root);
    QFile f(filePath);
    if (!f.open(QIODevice::WriteOnly)) return false;
    f.write(doc.toJson(QJsonDocument::Indented));
    f.close();

    m_isModified = false;
    return true;
}

// 打开
bool PlcModule::openFromFile(const QString& filePath)
{
    QFile f(filePath);
    if (!f.open(QIODevice::ReadOnly))
    {
        return false;
    }

    const auto doc = QJsonDocument::fromJson(f.readAll());
    f.close();
    if (!doc.isObject())
    {
        return false;
    }

    resetDiagram();

    const auto root = doc.object();
    const auto elemArray = root.value("elements").toArray();
    const auto connArray = root.value("connections").toArray();

    // 先创建所有元件
    QVector<PlcElement*> created;
    created.reserve(elemArray.size());

    for (const auto& v : elemArray)
    {
        const auto obj = v.toObject();
        auto* e = new PlcElement();
        e->setInstructionType(static_cast<PlcInstructionType>(obj.value("type").toInt()));
        e->setSize(obj.value("w").toDouble(60.0), obj.value("h").toDouble(35.0));
        e->setOn(obj.value("on").toBool(false));
        e->setPropPrefix(obj.value("prefix").toString("M"));
        e->setPropValue(obj.value("v1").toInt(0));
        e->setPropValue2(obj.value("v2").toInt(0));
        e->setPos(QPointF(obj.value("x").toDouble(0.0), obj.value("y").toDouble(0.0)));

        m_scene->addItem(e);
        created.push_back(e);
        // 信号连接
        connect(e, &PlcElement::writeCoilRequested, this, &PlcModule::onWriteCoilRequested);
        connect(e, &PlcElement::writeRegisterRequested, modbusManager_, &ModbusManager::writeRegister);
        connect(e, &PlcElement::logMessageRequested, this, [this](const QString& message) {
            if (modbusManager_)
            {
                modbusManager_->logMessage(message);
            }
        });

        // 监听地址变更，维护监控集合
        connect(e, &PlcElement::coilAddressChanged, this, [this](int oldAddr, int newAddr){
            if (!modbusManager_) return;
            modbusManager_->removeMonitoredCoil(oldAddr);
            modbusManager_->addMonitoredCoil(newAddr);
        });

        // 如果是线圈类元件，添加到监控列表
        if (e->propPrefix().compare("M", Qt::CaseInsensitive) == 0) {
            int address = e->propValue();
            qDebug() << "添加线圈到监控列表: M" << address;
            modbusManager_->addMonitoredCoil(address);
        }

        // 如果是比较元件，立即使用当前寄存器快照更新状态
        if (e->isComparator()) {
            e->updateCompareStateFromD(m_dRegisterSnapshot);
        }
    }

    // 创建连线
    for (const auto& v : connArray)
    {
        const auto obj = v.toObject();

        const bool sA = obj.value("s_anchor").toBool(false);
        const bool eA = obj.value("e_anchor").toBool(false);

        const int  si = obj.value("s_idx").toInt(-1);
        const int  ei = obj.value("e_idx").toInt(-1);
        const int  sside_i = obj.value("s_side").toInt(1); // 1=Right, 0=Left
        const int  eside_i = obj.value("e_side").toInt(0);

        const QPointF sAnchor(obj.value("s_ax").toDouble(0.0), obj.value("s_ay").toDouble(0.0));
        const QPointF eAnchor(obj.value("e_ax").toDouble(0.0), obj.value("e_ay").toDouble(0.0));

        ConnectionItem* c = nullptr;

        // 情况 1：两端都是元素
        if (!sA && !eA)
        {
            if (si < 0 || ei < 0 || si >= created.size() || ei >= created.size())
            {
                continue;
            }

            auto sside = sside_i ? ConnectionItem::Right : ConnectionItem::Left;
            auto eside = eside_i ? ConnectionItem::Right : ConnectionItem::Left;

            auto* start = created[si];
            auto* end   = created[ei];

            c = new ConnectionItem(start, sside, end, eside);
            start->addConnection(c);
            end->addConnection(c);
        }
        // 情况 2：start 是元素，end 是锚点（正常构造）
        else if (!sA && eA)
        {
            if (si < 0 || si >= created.size())
            {
                continue;
            }

            auto sside = sside_i ? ConnectionItem::Right : ConnectionItem::Left;
            auto* start = created[si];

            c = new ConnectionItem(start, sside, eAnchor);
            start->addConnection(c);
        }
        // 情况 3：start 是锚点，end 是元素（用“倒装法”：以元素为 start，把锚点当 endAnchor）
        else if (sA && !eA)
        {
            if (ei < 0 || ei >= created.size())
            {
                continue;
            }

            auto eside = eside_i ? ConnectionItem::Right : ConnectionItem::Left;
            auto* start = created[ei];   // 注意：把“原来的 end 元素”作为构造的 start
            c = new ConnectionItem(start, eside, sAnchor);
            start->addConnection(c);
        }
        else
        {
            continue;
        }

        if (!c)
        {
            continue;
        }

        // 折线处理
        const bool poly = obj.value("poly").toBool(false);
        if (poly)
        {
            const QPointF bend(obj.value("bx").toDouble(0.0), obj.value("by").toDouble(0.0));
            c->setPolylineMode(true, bend);
        }

        m_scene->addItem(c);
        c->updatePosition();
    }

    m_isModified = false;
    applyDRegisterSnapshot(m_dRegisterSnapshot); // 打开文件后立即应用当前快照
    return true;
}
